package com.rizzo.back.aop;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Advice that provides the functionality for caching the returning objects of some method.
 */
public class MethodCacheAdvice {

    private static final Logger LOGGER = LoggerFactory.getLogger(MethodCacheAdvice.class);

    private Cache cache;

    /**
     * Method cacheObject simply caches the outcome
     *
     * @param proceedingJoinPoint of type ProceedingJoinPoint
     * @return Object
     * @throws Throwable when
     */
    public Object cacheObject(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result;
        String cacheKey = this.getCacheKey(proceedingJoinPoint);
        Element element = cache.get(cacheKey);
        LOGGER.debug(new StringBuilder("MethodCacheAspect invoked! Get:").append(cacheKey).append(" - value:").append(element).toString());
        if (element == null) {
            result = proceedingJoinPoint.proceed();
            element = new Element(cacheKey, result);
            cache.put(element);
            LOGGER.info(new StringBuilder("MethodCacheCacheAspect invoked! Put:").append(cacheKey).append(" - value:").append(result).toString());
        }
        return element.getValue();
    }

    /**
     * Method getCacheKey creates a unique key to be stored in the cache.
     *
     * @param pjp of type ProceedingJoinPoint
     * @return String
     */
    private String getCacheKey(ProceedingJoinPoint pjp) {
        String targetName = pjp.getTarget().getClass().getSimpleName();
        String methodName = pjp.getSignature().getName();
        Object[] arguments = pjp.getArgs();
        StringBuilder sb = new StringBuilder();
        sb.append(targetName).append(".").append(methodName);
        if ((arguments != null) && (arguments.length != 0)) {
            for (Object argument : arguments) {
                sb.append(".").append(argument);
            }
        }
        return sb.toString();
    }

    /**
     * Method setCache sets the cache of this MethodCacheAdvice object
     *
     * @param cache of type Cache
     */
    public void setCache(Cache cache) {
        this.cache = cache;
    }

}